<script lang="tsx">
import { Nullable, PropType, Recordable } from '@/global'
import { isBoolean, isFunction } from '@/utils/is'
import { computed, defineComponent, unref } from 'vue'
import { FormProps, FormSchema } from '../types/form'
import { componentMap } from '../componentMap'
import { getSlot } from '@/utils/helper/tsxHelper'
import { ElCol, ElFormItem } from 'element-plus'

export default defineComponent({
  name: 'BasicFormItem',
  inheritAttrs: false,
  components: { ElCol, ElFormItem },
  props: {
    schema: {
      type: Object as PropType<FormSchema>,
      default: () => ({})
    },
    formProps: {
      type: Object as PropType<FormProps>,
      default: () => ({})
    },
    allDefaultValues: {
      type: Object as PropType<Recordable>,
      default: () => ({})
    },
    formModel: {
      type: Object as PropType<Recordable>,
      default: () => ({})
    },
    setFormModel: {
      type: Function as PropType<(key: string, value: any) => void>,
      default: null
    }
  },
  setup(props, { slots }) {
    // 获取组件配置
    const getValues = computed(() => {
      const { allDefaultValues, formModel, schema } = props
      return {
        field: schema.field,
        model: formModel,
        values: {
          ...allDefaultValues,
          ...formModel
        } as Recordable,
        schema: schema
      }
    })
    // 组件属性
    const getComponentsProps = computed(() => {
      const { schema, formModel } = props
      const { componentProps = {} } = schema
      if (!isFunction(componentProps)) {
        return componentProps
      }
      return componentProps({ schema, formModel }) ?? {}
    })
    // 显示/隐藏
    function getShow(): { isShow: boolean; isIfShow: boolean } {
      const { show, ifShow } = props.schema
      const { showAdvancedButton } = props.formProps
      const itemIsAdvanced = showAdvancedButton
        ? isBoolean(props.schema.isAdvanced)
          ? props.schema.isAdvanced
          : true
        : true
      let isShow = true
      let isIfShow = true

      if (isBoolean(show)) {
        isShow = show
      }
      if (isBoolean(ifShow)) {
        isIfShow = ifShow
      }
      if (isFunction(show)) {
        isShow = show(unref(getValues))
      }
      if (isFunction(ifShow)) {
        isIfShow = ifShow(unref(getValues))
      }
      isShow = isShow && itemIsAdvanced
      return { isShow, isIfShow }
    }
    function renderComponent() {
      const { component, field, changeEvent = 'Change' } = props.schema
      const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>
      const on = {
        ['on' + changeEvent]: (e: Nullable<Recordable>) => {
          if (comProps[changeEvent]) {
            comProps[changeEvent](e)
          }
          const target = e ? e.target : null
          const value = target ? target.value : e
          props.setFormModel(field, value)
        }
      }
      const comProps = {
        ...on,
        ...unref(getComponentsProps),
        modelValue: props.formModel[field]
      }
      return <Comp {...comProps}></Comp>
    }
    // form-item
    function renderItem() {
      const { slot, render, prop, label, rules, required, labelWidth, inlineMessage } = props.schema
      const getContent = () => {
        return slot
          ? getSlot(slots, slot, unref(getValues))
          : render
          ? render(unref(getValues))
          : renderComponent()
      }
      return (
        <el-form-item
          prop={prop}
          label={label}
          rules={rules}
          required={required}
          labelWidth={labelWidth || props.formProps.labelWidth}
          inlineMessage={inlineMessage}
        >
          {getContent()}
        </el-form-item>
      )
    }
    return () => {
      const { colProps = {}, component } = props.schema
      if (!componentMap.has(component)) return null
      const { baseColProps = {} } = props.formProps
      const realColProps = { ...baseColProps, ...colProps }
      const { isIfShow, isShow } = getShow()
      return (
        isIfShow && (
          <el-col {...realColProps} v-show={isShow}>
            {renderItem()}
          </el-col>
        )
      )
    }
  }
})
</script>
